<?php

namespace yphp;
/**
 * Class View
 * @package yphp
 * 视图类
 */
abstract class Compile extends Instance
{
    # 分隔符替换后的路径
    protected $path;
    # 视图源文件路径
    protected $view_path;
    # 编译后的文件路径
    protected $compile_path;
    # 标记
    static $sign = 0;

    // 将目录转化为分隔符
    protected function changePath($path)
    {
        return str_replace(['/', '.'], DS, $path);
    }

    // 获取视图文件路径
    protected function getview()
    {
        return VIEW . $this->path . '.html';
    }

    // 获取静态文件路径
    protected abstract function getHtml($path);

    // 获取视图文件编译路径
    protected abstract function getCompile($path);

    // 判断编译文件是否有效
    protected function isCompileUse($compile_path, $view_path)
    {
        return is_file($compile_path) && filemtime($compile_path) >= filemtime($view_path);
    }

    // 获取静态页结果
    function html($assign, $cache)
    {
        is_null($cache) && $cache = Config::web('static_cache_time');
        $html = $this->getHtml($this->path);
        if (DealFile::cacheFile($html, $cache)) {
            return file_get_contents($html);
        } else {
            $data = is_array($assign) ? $assign : (is_null($assign) ? [] : $assign());
            $content = Course::instance(function () use ($data) {
                extract($data);
                include $this->compile_path;
            })->buffer();
            $cache > 0 && DealFile::writeFile($html, $content, true);
            return $content;
        }

    }

    // 视图编译
    protected function compile($content)
    {
        self::compileCsrf($content);
        self::compileExtends($content);
        self::compilePhpOper($content);
        self::compileForeach($content);
        self::compileVolist($content);
        self::compileFor($content);
        self::compileIf($content);
        self::compileHtmlNotes($content);
        self::compilePhpNotes($content);
        self::compileNotHmtl($content);
        self::compileEcho($content);
        self::compileVariable($content);
        self::notCompile($content);
        self::compileInclude($content);
        return $content;
    }

    //+------------------------------------------------------
    // 常用编译模板
    //+------------------------------------------------------

    // CSRF模板  {@CSRF_TOKEN}
    static function compileCsrf(&$str)
    {
        $str = preg_replace('/({@CSRF_TOKEN})/','<?php echo \yphp\Safe::csrf_token(); ?>',$str);
    }

    // php操作模板 {<  >} => <?php ?\>
    static function compilePhpOper(&$str)
    {
        $str = preg_replace('/{<([^}]+)>}/', '<?php \1; ?>', $str);
    }

    // HTML注释模板  {* *} => <!- -->
    static function compileHtmlNotes(&$str)
    {
        $str = preg_replace('/{\*([^}]+)\*}/', '', $str);
    }

    // 注释模板  {& &} => /* */
    static function compilePhpNotes(&$str)
    {
        $str = preg_replace('/{&([^}]+)&}/', '/*\1*/', $str);
    }

    // Echo模板 {: } => <?php echo ?\>
    static function compileEcho(&$str)
    {
        $str = preg_replace_callback('/{:([^}]+)}/', function ($match) {
            return '<?php echo htmlspecialchars(' . self::compileArr($match[1]) . '); ?>';
        }, $str);
    }

    // 输出变量（含数组） { $a } => <?php  ?\>
    static function compileVariable(&$str)
    {
        $str = preg_replace_callback('/{\s*\$(.*?)\s*}/', function ($match) {
            return '<?php echo htmlspecialchars($' . self::compileArr($match[1]) . '); ?>';
        }, $str);
    }

    // 不转换实体html
    static function compileNotHmtl(&$str)
    {
        $str = preg_replace_callback('/{\s*!\$(.*?)\s*}/', function ($match) {
            return '<?php echo $' . self::compileArr($match[1]) . '; ?>';
        }, $str);
    }

    // 不编译输出
    static function notCompile(&$str)
    {
        $str = preg_replace('/{@([^}]+)}/', '\1', $str);
    }

    // 特殊数组处理
    static function compileArr($match)
    {
        $text = preg_replace_callback('/\.(\$?\w+)/', function ($m) {
            return '[' . ($m[1][0] === '$' ? $m[1] : ("'{$m[1]}'")) . ']';
        }, $match);
        return $text;
    }

    // foreach模板
    static function compileForeach(&$str)
    {
        $str = preg_replace_callback('/{foreach\s+(.+?)\s*}(.*?){\/foreach}/us', function ($match) {
            return '<?php foreach(' . self::compileArr($match[1]) . ') { ?>' . $match[2] . '<?php } ?>';
        }, $str);
    }

    // if模板
    static function compileIf(&$str)
    {
        $str = preg_replace_callback('/{if\s+(.+?)\s*}/', function ($match) {
            return '<?php if(' . self::compileArr($match[1]) . ') {  ?>';
        }, $str);
        $str = preg_replace_callback('/{elseif\s+(.+?)\s*}/', function ($match) {
            return '<?php } elseif(' . self::compileArr($match[1]) . ') { ?>';
        }, $str);
        $str = preg_replace('/{else}/', '<?php } else { ?>', $str);
        $str = preg_replace('/{\/if}/', '<?php } ?>', $str);
    }

    // volist解析
    static function compileVolist(&$str)
    {
        $str = preg_replace_callback('/{volist\s*(.*?)\s*}(.*?){\/volist}/us', function ($match) {
            $num = ++self::$sign;
            $default = ['key' => 'k', 'length' => PHP_INT_MAX, 'offset' => 0];
            $need = ['name', 'item'];
            $arr = explode(' ', preg_replace('/\s+/', ' ', $match[1]));
            foreach ($arr as $v) {
                list($key, $val) = explode('=', $v);
                if (in_array($key, $need)) {
                    $need = array_diff($need, [$key]);
                }
                $default[$key] = substr($val, 1, -1);
            }
            if (count($need) > 0) throw new \Exception('volist缺少必须的属性' . join(',', $need));

            $vnum = '$__volist_len_' . $num;
            $vindex = '$__volist_index_' . $num;
            return "<?php $vnum = $vindex = 0; foreach ({$default['name']} as \${$default['key']} => \${$default['item']}):  if ($vindex++ < {$default['offset']}) continue; if ($vnum++ >=  {$default['length']}) break;  ?>{$match[2]}<?php endforeach; ?>";

        }, $str);
    }

    // for解析
    static function compileFor(&$str)
    {
        $str = preg_replace_callback('/{for\s+(.+?)\s*}(.*?){\/for}/us', function ($match) {
            return '<?php for(' . self::compileArr($match[1]) . ') { ?>' . $match[2] . '<?php } ?>';
        }, $str);
    }

    // include解析 {include file="public/header" /}  => <?php include 'public/header.html' ?\>
    static function compileInclude(&$str)
    {
        $str = preg_replace_callback('/{include\s*(.*?)\s*}/', function ($match) {
            $list = ['data' => 'null', 'time' => 'null'];
            foreach (explode(' ', preg_replace('/\s+/', ' ', $match[1])) as $v) {
                list($key, $val) = explode('=', $v);
                $list[$key] = $key === 'file' ? substr($val, 1, -1) : self::compileArr(substr($val, 1, -1));
            }
            if(!key_exists('file', $list)) throw new \Exception('include缺少必要的属性：file');
            return "<?php echo \yphp\View::instance('{$list['file']}')->html({$list['data']},{$list['time']}); ?>";
        }, $str);
    }

    // ----------------------------------------------------------------------
    //  继承模板编译
    // ----------------------------------------------------------------------

    protected function compileExtends(&$str)
    {
        $str = preg_replace_callback('/<extends\s*(.*?)\s*>(.*?)<\/extends>/us', function ($match) {
            $list = self::parseAttr(['data' => 'null', 'time' => 'null'], $match[1], ['file']);
            if (!key_exists('file', $list)) throw new \Exception('<extends>缺少必要的属性：file');
            $change = [];
            preg_replace_callback('/<change\s*name=([\'"])(.+?)\1\s*>(.*?)<\/change>/us', function ($match) use (&$change) {
                $change[$match[2]] = $match[3];
            }, $match[2]);
            return "<?php echo \yphp\Template::instance('{$list['file']}', '" . Safe::en($this->path) . "', '" . base64_encode(serialize($change)) . "')->html({$list['data']}, {$list['time']}); ?>";
        }, $str);
    }

    // 标签模板属性键值补充
	static function parseAttr($list, $match, $filter = [])
	{
	    if ($match) {
	        foreach (preg_split('/\s+/', $match) as $v) {
	            list($key, $val) = explode('=', $v);
	            $list[$key] = in_array($key, $filter) ? substr($val, 1, -1) : self::parseArr(substr($val, 1, -1));
	        }
	    }
	    return $list;
	}

	// 检索数组并处理
	static function parseArr($match)
	{
	    return preg_replace_callback('/\.(\$?\w+)/', function ($m) {
	        return '[' . ($m[1][0] === '$' ? $m[1] : ("'$m[1]'")) . ']';
	    }, $match);
	}
	
}